using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Web.Script.Serialization;
using System.Web.UI;
namespace JQueryControlToolkit.Controls
{
    [NonVisualControl, ParseChildren(true), PersistChildren(false)]
    public abstract class JQueryExtender : Control, IAttributeAccessor, JSObject
    {
        private Dictionary<string, JQueryElementAttribute> propertyMetadata = new Dictionary<string, JQueryElementAttribute>();
        private Dictionary<string, List<string>> jqueryPropertiesMapping = new Dictionary<string, List<string>>();
        private JavaScriptSerializer serializer = new JavaScriptSerializer();
        private Dictionary<string, object> properties = new Dictionary<string, object>();
        protected JQuery jquery;
        private JQuery destroy = null;
        public bool Enabled
        {
            get;
            set;
        }
        public bool WaitForPageReady
        {
            get;
            set;
        }
        public bool RunAgainAfterUpdatePanelRefreshed
        {
            get;
            set;
        }
        protected abstract string jQueryFunction
        {
            get;
        }
        public string TargetControlSelector
        {
            get;
            set;
        }
        public JQueryExtender()
        {
            this.Enabled = true;
            this.propertyMetadata.Add("TargetControlSelector", new JQueryElementAttribute(true));
            PropertyInfo[] array = base.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
            for (int i = 0; i < array.Length; i++)
            {
                PropertyInfo pi = array[i];
                if (pi.DeclaringType.IsSubclassOf(typeof(JQueryExtender)) || !(pi.Name != "ID"))
                {
                    JQueryElementAttribute[] attributes = (JQueryElementAttribute[])pi.GetCustomAttributes(typeof(JQueryElementAttribute), true);
                    if (attributes.Length == 1)
                    {
                        this.propertyMetadata.Add(pi.Name, attributes[0]);
                        this.AddMapping(attributes[0].JQueryElementName, pi.Name);
                    }
                    else
                    {
                        this.propertyMetadata.Add(pi.Name, new JQueryElementAttribute(pi.Name, false));
                        this.AddMapping(pi.Name, pi.Name);
                    }
                    MethodInfo getPi = pi.GetGetMethod(true);
                    if (getPi == null)
                    {
                        throw new NotSupportedException("The get accessor on the property named " + pi.Name + " could not be found");
                    }
                    getPi.Invoke(this, null);
                }
            }
        }
        private void AddMapping(string jQueryPropertyName, string serverControlPropetyName)
        {
            if (!this.jqueryPropertiesMapping.ContainsKey(jQueryPropertyName))
            {
                this.jqueryPropertiesMapping.Add(jQueryPropertyName, new List<string>());
            }
            this.jqueryPropertiesMapping[jQueryPropertyName].Add(serverControlPropetyName);
        }
        protected void ChangeMetadata(string propertyName, JQueryElementAttribute attribute)
        {
            if (string.IsNullOrEmpty(attribute.PropertyName))
            {
                attribute.PropertyName = propertyName;
            }
            this.propertyMetadata[propertyName] = attribute;
        }
        protected override object SaveControlState()
        {
            return new Pair(base.SaveControlState(), this.properties);
        }
        protected override void LoadControlState(object savedState)
        {
            Pair p = savedState as Pair;
            if (p != null && p.Second is Dictionary<string, object>)
            {
                base.LoadControlState(p.First);
                this.properties = (Dictionary<string, object>)p.Second;
            }
            else
            {
                base.LoadControlState(savedState);
            }
        }
        protected T GetPropertyValue<T>(string propertyName, T defaultNullValue)
        {
            T result;
            if (this.properties.ContainsKey(propertyName))
            {
                object propertyValue = this.properties[propertyName];
                if (propertyValue is T)
                {
                    result = (T)((object)propertyValue);
                }
                else
                {
                    if (propertyValue == null)
                    {
                        result = (T)((object)this.propertyMetadata[propertyName].DefaultValue);
                    }
                    else
                    {
                        if (typeof(T).IsEnum)
                        {
                            result = (T)((object)Enum.Parse(typeof(T), propertyValue.ToString()));
                        }
                        else
                        {
                            result = (T)((object)Convert.ChangeType(propertyValue, typeof(T)));
                        }
                    }
                }
            }
            else
            {
                this.SetDefaultPropertyValue<T>(propertyName, defaultNullValue);
                result = this.GetPropertyValue<T>(propertyName, defaultNullValue);
            }
            return result;
        }
        private void SetDefaultPropertyValue<T>(string propertyName, T defaultNullValue)
        {
            this.SetPropertyValue<T>(propertyName, defaultNullValue);
            this.propertyMetadata[propertyName].DefaultValue = defaultNullValue;
        }
        protected void SetPropertyValue<T>(string propertyName, T value)
        {
            if (this.propertyMetadata.ContainsKey(propertyName) && this.jqueryPropertiesMapping.ContainsKey(this.propertyMetadata[propertyName].JQueryElementName))
            {
                List<string> properties = this.jqueryPropertiesMapping[this.propertyMetadata[propertyName].JQueryElementName];
                if (properties.Count > 1)
                {
                    foreach (string property in properties)
                    {
                        if (!(property == propertyName))
                        {
                            if (this.properties.ContainsKey(property) && this.properties[property] != null)
                            {
                                throw new NotSupportedException(string.Format("The property named '{0}' is mapped more than once ({2}) with the jquery property '{1}', thus, you may not define it twice with difference values", propertyName, this.propertyMetadata[propertyName].JQueryElementName, string.Join(",", properties.ToArray())));
                            }
                        }
                    }
                }
            }
            this.properties[propertyName] = value;
        }
        protected JQuery EnsureDestroy()
        {
            if (this.destroy == null)
            {
                this.destroy = this.RenderDestroy();
            }
            return this.destroy;
        }
        public virtual JQuery RenderDestroy()
        {
            JQuery jquery;
            if (this.propertyMetadata.ContainsKey("TargetControlSelector"))
            {
                jquery = new JQuery(this.propertyMetadata["TargetControlSelector"].GetValue(this, this.TargetControlSelector).ToString());
            }
            else
            {
                jquery = new JQuery(this.TargetControlSelector);
            }
            return jquery.CallPlugIn(base.GetType().Name + ":Destroy", this.jQueryFunction, DomResult.Escape("destroy"));
        }
        protected virtual JQuery EnsureFunctionCall()
        {
            if (this.jquery == null)
            {
                this.jquery = this.RenderFunctionCall(this.jquery);
            }
            return this.jquery;
        }
        protected JQuery GetJQuery()
        {
            JQuery result;
            JQuery jQuery;
            if (this.propertyMetadata.ContainsKey("TargetControlSelector"))
            {
                object selector = this.propertyMetadata["TargetControlSelector"].GetValue(this, this.TargetControlSelector);
                if (selector == null)
                {
                    result = null;
                    return result;
                }
                jQuery = new JQuery(selector.ToString());
            }
            else
            {
                jQuery = new JQuery(this.TargetControlSelector);
            }
            result = jQuery;
            return result;
        }
        public virtual JQuery RenderFunctionCall(JQuery jquery)
        {
            if (jquery == null)
            {
                jquery = this.GetJQuery();
            }
            if (jquery != null && !string.IsNullOrEmpty(this.jQueryFunction))
            {
                jquery = jquery.CallPlugIn(base.GetType().Name, this.jQueryFunction, this.EvaluateParameters(this.properties));
            }
            return jquery;
        }
        protected virtual Dictionary<string, object> EvaluateParameters()
        {
            return this.EvaluateParameters(this.properties);
        }
        protected virtual Dictionary<string, object> EvaluateParameters(Dictionary<string, object> properties)
        {
            Dictionary<string, object> parameters = new Dictionary<string, object>();
            foreach (KeyValuePair<string, object> param in properties)
            {
                JQueryElementAttribute attribute;
                if (this.propertyMetadata.ContainsKey(param.Key))
                {
                    attribute = this.propertyMetadata[param.Key];
                }
                else
                {
                    attribute = new JQueryElementAttribute(param.Key, false);
                }
                if (param.Value != null && !param.Value.Equals(attribute.DefaultValue))
                {
                    object value = attribute.GetValue(this.NamingContainer ?? this.Page, param.Value);
                    string stringValue = null;
                    if (param.Value is JSObject)
                    {
                        stringValue = ((JSObject)value).ToScript();
                    }
                    if (stringValue == null && value != null)
                    {
                        stringValue = value.ToString();
                    }
                    if ((stringValue == null && value is bool) || value.GetType().IsEnum)
                    {
                        stringValue = stringValue.ToLower();
                    }
                    if (stringValue == "true" || stringValue == "false")
                    {
                        attribute.SerializeAs = SerializeAs.Is;
                    }
                    if (!attribute.IsMatch(stringValue))
                    {
                        if (attribute.IsSelector)
                        {
                            throw new ArgumentException(string.Format("The selector '{0}' provided for the property {1} does not meet the requirements", stringValue, param.Key));
                        }
                        throw new ArgumentException(string.Format("The value '{0}' provided for the property {1} does not meet the requirements : {2}", stringValue, param.Key, attribute.RegexValidation));
                    }
                    else
                    {
                        if (attribute.SerializeAs == SerializeAs.String)
                        {
                            parameters.Add(attribute.JQueryElementName, stringValue);
                        }
                        else
                        {
                            if (param.Value.GetType().IsPrimitive || value is string || value is JSObject)
                            {
                                JSFunctionString functionString = value as JSFunctionString;
                                if (functionString == null || !string.IsNullOrEmpty(functionString.String))
                                    parameters.Add(attribute.JQueryElementName, value);
                            }
                            else
                            {
                                parameters.Add(attribute.JQueryElementName, value);
                            }
                        }
                    }
                }
            }
            return parameters;
        }
        public void RegisterFunction(JQueryHandler jqueryMethod)
        {
            this.AddJQuery(jqueryMethod());
        }
        protected void AddJQuery(JQuery jquery)
        {
            if (!this.RunAgainAfterUpdatePanelRefreshed)
            {
                if (ScriptManager.GetCurrent(this.Page) == null || !ScriptManager.GetCurrent(this.Page).IsInAsyncPostBack)
                {
                    JQueryPageExtensions.Add(this.Page, jquery, this.WaitForPageReady);
                }
            }
            else
            {
                JQueryPageExtensions.AddAjaxLoad(this.Page, jquery, this.WaitForPageReady);
            }
        }
        protected override void Render(HtmlTextWriter writer)
        {
            if (this.Enabled)
            {
                JQuery jquery = this.EnsureFunctionCall();
                if (!this.WaitForPageReady && !this.RunAgainAfterUpdatePanelRefreshed)
                {
                    writer.WriteBeginTag("script");
                    writer.WriteAttribute("type", "text/javascript");
                    writer.Write(">");
                    jquery.ToScript(writer);
                    writer.WriteEndTag("script");
                }
                else
                {
                    this.AddJQuery(jquery);
                }
            }
        }
        public string GetAttribute(string key)
        {
            return this.GetPropertyValue<string>(key, null);
        }
        public void SetAttribute(string key, string value)
        {
            this.SetPropertyValue<string>(key, value);
        }
        public string ToScript()
        {
            return Extensions.ToScript(this);
        }
        public void ToScript(TextWriter toScript)
        {
            JQuery jq = this.EnsureFunctionCall();
            if (jq != null)
            {
                jq.ToScript(toScript);
            }
            else
            {
                DomOperation.ToScript<object>(toScript, this.EvaluateParameters());
            }
        }
    }
}
